#include "Socket.h"
#include <iostream>
#include <string.h>
#include <errno.h>
#include <fcntl.h>


Socket::Socket() : m_sock ( -1 ) , m_connection_in_progress(false)
{
	//cout << "consctructor: " << m_sock << endl;
	memset ( &m_addr, 0, sizeof ( m_addr ) );
	memset ( &m_addr4, 0, sizeof ( m_addr4 ) );
}

Socket::~Socket()
{
	if ( is_valid() )
	{
		cout << __LINE__ << " ~Socket was called and " << m_sock << " closed~ "<< endl;
		::close ( m_sock );
	}
}

bool Socket::create()
{
	m_sock = socket ( AF_INET6, SOCK_STREAM, 0 );

	cout << "create socket: " << m_sock << "in file " << __FILE__ << " line " << __LINE__ << endl;
	if ( ! is_valid() )
	{
		return false;
	}

	//TIME_WAIT - argh
	int on = 1;
	if ( setsockopt ( m_sock, SOL_SOCKET, SO_REUSEADDR, ( const char* ) &on, sizeof ( on ) ) == -1 )
	{
		return false;
	}

	return true;
}

bool Socket::create4()
{
	m_sock = socket ( AF_INET, SOCK_STREAM, 0 );

	if ( ! is_valid() )
		return false;

	int on = 1;
	if ( setsockopt ( m_sock, SOL_SOCKET, SO_REUSEADDR, ( const char* ) &on, sizeof ( on ) ) == -1 )
	{
		return false;
	}

	return true;
}

bool Socket::bind ( const int port )
{
	if ( ! is_valid() )
	{
		return false;
	}

	m_addr.sin6_family = AF_INET6;
	m_addr.sin6_addr = in6addr_any;
	m_addr.sin6_port = htons ( port );

	int bind_return = ::bind ( m_sock, ( struct sockaddr * ) &m_addr, sizeof ( m_addr ) );
	if ( bind_return == -1 )
	{
		return false;
	}

	return true;
}

bool Socket::bind4 ( const int port )
{
	if ( ! is_valid() )
	{
		return false;
	}

	m_addr4.sin_family = AF_INET;
	m_addr4.sin_addr.s_addr = INADDR_ANY;
	m_addr4.sin_port = htons ( port );

	int bind_return = ::bind ( m_sock,( struct sockaddr * ) &m_addr4,sizeof ( m_addr4 ) );

	if ( bind_return == -1 )
	{
		return false;
	}
	return true;
}

bool Socket::listen() const
{
	if ( ! is_valid() )
	{
		return false;
	}

	int listen_return = ::listen ( m_sock, MAXCONNECTIONS );

	if ( listen_return == -1 )
	{
		return false;
	}

	return true;
}


bool Socket::accept ( Socket& new_socket ) const
{
	int addr_length = sizeof ( m_addr4 );
	//may it?
	new_socket.m_sock = ::accept ( m_sock, ( sockaddr * ) &m_addr4, ( socklen_t * ) &addr_length );
	if ( new_socket.m_sock <= 0 )
	{
		return false;
	}
	else
	{
		return true;
	}
}


bool Socket::send ( const std::string s ) const
{
	//int status = ::send ( m_sock, s.c_str(), s.size(), MSG_NOSIGNAL );
	//cout << "send1: " << this->sockfd() << endl;
	//cout << "send socket is: " << m_sock << endl;
	int status = ::send ( m_sock, s.c_str(), s.size(), 0 );
	//cout << "send2: " << this->sockfd() << endl;
	if ( status == -1 )
	{
		if (errno)
		{
			throw SocketException (strerror(errno));
		}
		return false;
	}
	else
	{
		if (errno)
		{
			throw SocketException (strerror(errno));
		}
		return true;
	}
}


int Socket::recv ( std::string& s ) const
{
	char buf [ MAXRECV + 1 ];

	s = "";

	memset ( buf, 0, MAXRECV + 1 );

	cout << "recv socket is: " << m_sock << endl;
	int status = ::recv ( m_sock, buf, MAXRECV, 0 );
	//cout << "status is : " << status << endl;
	if ( status == -1 )
	{
		if (errno)
		{
			throw SocketException (strerror(errno));
		}

	}
	else if ( status == 0 )
	{
		if (errno)
		{
			throw SocketException (strerror(errno));
		}
	}
	else
	{
		s.append(buf, status);
	}
	return status;
}

bool Socket::connect ( const std::string host, const int port )
{
	if ( ! is_valid() ) return false;

	m_addr.sin6_family = AF_INET6;
	m_addr.sin6_port = htons ( port );

	int status = inet_pton ( AF_INET6, host.c_str(), &m_addr.sin6_addr );

	if ( errno == EAFNOSUPPORT )
	{
		cout << "Family not supported." << endl;
		return false;
	}
	
	cout << "connect socket is: " << m_sock << ", sizeof(m_addr) is " << sizeof(m_addr) << endl;
	status = ::connect ( m_sock, (sockaddr*) &m_addr, sizeof(m_addr) );
	if ( status == 0 )
	{
		//连接成功
		return true;
	}
	else
	{
		if ( errno == EINPROGRESS )
		{
			m_connection_in_progress = true;
		}
		throw SocketException (strerror(errno));
	}
}

void Socket::set_non_blocking ( const bool b )
{
	int opts;
	opts = fcntl ( m_sock, F_GETFL );

	if ( opts < 0 )
	{
		return;
	}

	if ( b )
	{
		opts = ( opts | O_NONBLOCK );
	}
	else
	{
		opts = ( opts & ~O_NONBLOCK );
	}
	fcntl ( m_sock,	F_SETFL,opts );
}

bool Socket::got_no_error_on_socket ()
{
		int error;
		int n = sizeof(error);
		if (getsockopt(m_sock, SOL_SOCKET, SO_ERROR, &error, (socklen_t*)&n) < 0 ||	error != 0) 
		{
			errno = error;
			throw SocketException (strerror(errno));
		}
		m_connection_in_progress = false;
		return true;
}

bool Socket::connection_in_progress () const
{
	if (m_connection_in_progress)
	{
		return true;
	}
	return false;
}
